using System;
using System.Collections.Generic;
using System.Text;

namespace OliveChess
{
    class Board //: IChessBoard
    {
        // TODO put those constant values in a separate class

        //private int turn;
        private bool turn;

        private Piece[] pieces;
        public int lastEnPassant;
        
        public bool whiteCanCastleKingSide;
        public bool whiteCanCastleQueenSide;
        public bool blackCanCastleQueenSide;
        public bool blackCanCastleKingSide;
        public int lastBlackPawnMove;
        public int lastWhitePawnMove;



        //private String startPosition = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";

        public Board()
        {
            pieces = new Piece[64];
            Clear();
        }

        public void Clear()
        {
            for (int i = 0; i < 64; i++)
            {
                pieces[i] = Piece.EMPTY;
            }
        }

        public void Clear(int square)
        {
            pieces[square] = Piece.EMPTY;
        }

        /*
        * Create a new board by copy
        */
        public Board(Board p)
        {
            pieces = new Piece[64];
            for (int i = 0; i < 64; i++)
            {
                SetPiece(p.GetPiece(i),i);
            }
            turn = p.turn;

            whiteCanCastleKingSide = p.whiteCanCastleKingSide;
            whiteCanCastleQueenSide = p.whiteCanCastleQueenSide;
            blackCanCastleQueenSide = p.blackCanCastleQueenSide;
            blackCanCastleKingSide = p.blackCanCastleKingSide;

            lastEnPassant = p.lastEnPassant;

            lastBlackPawnMove = p.lastBlackPawnMove;
            lastWhitePawnMove = p.lastBlackPawnMove;
        }

        public void SetFromFEN(String fenString)
        {
            this.Clear();
            int square = 56;

            string[] parameters = fenString.Split(' ');
            String board = parameters[0];
            char ply = parameters[1][0];
            String castling = parameters[2];
            String enPassant = parameters[3];
            String halfMoveClock = parameters[4];
            String fullMoveNumber = parameters[5];

            foreach (char carBoard in board)
            {
                switch (carBoard)
                {
                    case 'K':
                        this.SetPiece(Piece.WK, square);
                        square++;
                        break;
                    case 'Q':
                        this.SetPiece(Piece.WQ, square);
                        square++;
                        break;
                    case 'R':
                        this.SetPiece(Piece.WR, square);
                        square++;
                        break;
                    case 'N':
                        this.SetPiece(Piece.WN, square);
                        square++;
                        break;
                    case 'B':
                        this.SetPiece(Piece.WB, square);
                        square++;
                        break;
                    case 'P':
                        this.SetPiece(Piece.WP, square);
                        square++;
                        break;
                    case 'k':
                        this.SetPiece(Piece.BK, square);
                        square++;
                        break;
                    case 'q':
                        this.SetPiece(Piece.BQ, square);
                        square++;
                        break;
                    case 'r':
                        this.SetPiece(Piece.BR, square);
                        square++;
                        break;
                    case 'n':
                        this.SetPiece(Piece.BN, square);
                        square++;
                        break;
                    case 'b':
                        this.SetPiece(Piece.BB, square);
                        square++;
                        break;
                    case 'p':
                        this.SetPiece(Piece.BP, square);
                        square++;
                        break;
                    case '/':
                        square-=16;
                        break;
                    case '1':
                        square += 1;
                        break;
                    case '2':
                        square += 2;
                        break;
                    case '3':
                        square += 3;
                        break;
                    case '4':
                        square += 4;
                        break;
                    case '5':
                        square += 5;
                        break;
                    case '6':
                        square += 6;
                        break;
                    case '7':
                        square += 7;
                        break;
                    case '8':
                        square += 8;
                        break;
                    default:
                        // TODO Throw incorrect data exception
                        break;
                }
            }

            switch (ply)
            {
                case 'w':
                    this.SetWhiteToMove();
                    break;
                case 'b':
                    this.SetBlackToMove();
                    break;
                default:
                    // TODO Throw incorrect data exception
                    break;
            }


            this.whiteCanCastleKingSide = false;
            this.whiteCanCastleQueenSide = false;
            this.blackCanCastleKingSide = false;
            this.blackCanCastleQueenSide = false;
            foreach (char carCastle in castling)
            {
                switch (carCastle)
                {
                    case 'K':
                        this.whiteCanCastleKingSide = true;
                        break;
                    case 'Q':
                        this.whiteCanCastleQueenSide = true;
                        break;
                    case 'k':
                        this.blackCanCastleKingSide = true;
                        break;
                    case 'q':
                        this.blackCanCastleQueenSide = true;
                        break;
                    default:
                        // TODO Throw incorrect data exception
                        break;
                }

            }

            if (enPassant[0] != '-')
            {
                this.lastEnPassant = (enPassant[0]-'a') + 8*(enPassant[1]-'1');
            }

            this.lastWhitePawnMove = int.Parse(halfMoveClock);
            this.lastBlackPawnMove = int.Parse(fullMoveNumber);


        }

        /**
         * Set a piece to the given square
         * 
         * @param piece
         * @param square
         */
        public void SetPiece(Piece piece, int square)
        {
            pieces[square] = piece;
        }

        /**
         * Get the content of a given square
         * 
         * @param square
         * @return
         */
        public Piece GetPiece(int square)
        {
            return pieces[square];
        }

        public void SetWhiteToMove()
        {
            turn = true;
        }

        public void SetBlackToMove()
        {
            turn = false;
        }

        public bool IsWhiteToMove()
        {
            return turn;
        }

        public bool IsBlackToMove()
        {
            return !turn;
        }

        /*
         * Change the turn
         */
        public void ChangeTurn()
        {
            turn = !turn;
        }

        public bool GetTurn()
        {
            return turn;
        }

        /**
         * Indicates if the piece is playable for the given player (only check the
         * color)
         * 
         * @param square
         * @return
         */
        public bool IsPiecePlayable(int square)
        {
            Piece pieceCode = this.GetPiece(square);
            if (turn)
            {
                return IsPieceWhite(pieceCode);
            }
            else
            {
                return IsPieceBlack(pieceCode);
            }
        }

        public bool IsPieceWhite(Piece piece)
        {
            return (piece == Piece.WP || piece == Piece.WR || piece == Piece.WN
                    || piece == Piece.WB || piece == Piece.WQ || piece == Piece.WK);
        }

        public bool IsPieceBlack(Piece piece)
        {
            return (piece == Piece.BP || piece == Piece.BR || piece == Piece.BN
                    || piece == Piece.BB || piece == Piece.BQ || piece == Piece.BK);
        }

        /**
         * Get all the legal moves for the given position
         * @return
         */
        /*
        public List<Move> GetLegalMoves()
        {
            List<Move> legalMoves = Engine.GetLegalMoves(this);
            //DebugDisplayMoves(possibleMoves);
            return legalMoves;
        }
         */

        /*
        private void DebugDisplayPossibleMoves(ArrayList<Move> possibleMoves) {
            System.out.println("Possible moves:");
            for(Move m : possibleMoves){
                System.out.print(m.toString() + " ");
            }
            System.out.println();
        }
        */

        /**
         * Return if the square is empty or occupied by a piece of the opponent
         * 
         * @param square
         * @return
         */
        public bool IsEmptyOrOpponent(int square)
        {
            Piece piece = this.GetPiece(square);
            if(piece == Piece.EMPTY) return true;
            if (this.IsWhiteToMove()) return IsPieceBlack(piece);
            //if (this.IsBlackToMove()) return IsPieceWhite(piece);
            return IsPieceWhite(piece); //optim
        }
        public bool IsEmptyOrColor(int square, bool color)
        {
            Piece piece = this.GetPiece(square);
            if (piece == Piece.EMPTY) return true;
            if (color) return IsPieceWhite(piece);
            return IsPieceBlack(piece);
        }

        /**
         * Perform a basic move or basic capture (no change of the turn)
         * 
         * @param start
         * @param dest
         */
        private void Move(int startSquare, int destSquare)
        {
            pieces[destSquare] = pieces[startSquare];
            pieces[startSquare] = Piece.EMPTY;
        }

        /**
         * Perform the move and change turn
         * 
         * @param m
         */
        public void Play(Move m)
        {
            Piece piece = GetPiece(m.begin);
            Piece dest = GetPiece(m.end);
            // En-passant extra-move (remove opponant's pawn)
            if (dest == Piece.EMPTY && Square.GetCol(m.begin) != Square.GetCol(m.end))
            {
                if (piece == Piece.WP)
                {
                    this.SetPiece(Piece.EMPTY, Square.Get(Square.GetCol(m.begin), Square.GetRow(m.begin) + 1));
                }
                if (piece == Piece.BP)
                {
                    this.SetPiece(Piece.EMPTY, Square.Get(Square.GetCol(m.begin), Square.GetRow(m.begin) - 1));
                }
            }

            // Castleling extra-move (Rook)
            if (piece == Piece.WK && m.begin == Square.E1 && m.end == Square.G1)
            {
                this.Move(Square.H1, Square.F1);
            }
            if (piece == Piece.WK && m.begin == Square.E1 && m.end == Square.C1)
            {
                this.Move(Square.A1, Square.D1);
            }
            if (piece == Piece.BK && m.begin == Square.E8 && m.end == Square.G8)
            {
                this.Move(Square.H8, Square.F8);
            }
            if (piece == Piece.BK && m.begin == Square.E8 && m.end == Square.C8)
            {
                this.Move(Square.A8, Square.D8);
            }

            // Castleling - canceller
            if(piece == Piece.WK)
            {
                whiteCanCastleKingSide = false;
                this.whiteCanCastleQueenSide = false;
            } else
            if(piece == Piece.BK)
            {
                blackCanCastleKingSide = false;
                blackCanCastleQueenSide = false;
            } else
            if(piece == Piece.WR && m.begin == Square.A1)
            {
                this.whiteCanCastleQueenSide = false;
            } else
            if (piece == Piece.WR && m.begin == Square.H1)
            {
                whiteCanCastleKingSide = false;
            } else
            if (piece == Piece.BR && m.begin == Square.A8)
            {
                blackCanCastleQueenSide = false;
            } else
            if (piece == Piece.BR && m.begin == Square.H8)
            {
                blackCanCastleKingSide = false;
            }

            // keeping en-passant last move
            if((piece ==Piece.WP && (Square.GetRow(m.end)-Square.GetRow(m.begin))==2 )
                || (piece == Piece.BP && (Square.GetRow(m.end) - Square.GetRow(m.begin)) == 2))
            {
                lastEnPassant = Math.Max(m.begin, m.end) - Math.Min(m.begin, m.end);
            } else
            {
                lastEnPassant = 65;
            }
             
            // standard move
            this.Move(m.begin, m.end);

            // promotion
            if(m.promotion != '0')
            {
                switch (m.promotion)
                {
                    case 'q':
                        if(turn) this.SetPiece(Piece.WQ,m.end);
                        else this.SetPiece(Piece.BQ, m.end);
                        break;
                    case 'r':
                        if (turn) this.SetPiece(Piece.WR, m.end);
                        else this.SetPiece(Piece.BR, m.end);
                        break;
                    case 'b':
                        if (turn) this.SetPiece(Piece.WB, m.end);
                        else this.SetPiece(Piece.BB, m.end);
                        break;
                    case 'n':
                        if (turn) this.SetPiece(Piece.WN, m.end);
                        else this.SetPiece(Piece.BN, m.end);
                        break;
                }

            }

            this.ChangeTurn();
        }

        /**
         * Indicates if the square is empty
         * 
         * @param square
         * @return
         */
        public bool IsSquareEmpty(int square)
        {
            return (pieces[square] == Piece.EMPTY);
        }

        public void SetStartPosition()
        {
            this.SetFromFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
        }

        /*
        public override int GetHashCode()
        {
            int code = 0;
            for (int i = 0; i < 64; i++) code += ((int)this.pieces[i])*(13 ^ i);
            return code;
            
            //return this.pieces.GetHashCode();
        }
        */

        // TODO impove this method
        public String GetSimplifiedHashCode()
        {
            /*
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < 64; i++)
            {
                    result.Append((int)pieces[i]);
             }
                
            return result.ToString();*/
            return this.GetPseudoFEN();
        }


        private static char[] fenPieceCodes = {'P','N','B','R','Q','K','p','n','b','r','q','k' };

        /*
         * Unique key generator for positions
         */
        public String GetPseudoFEN()
        {
            StringBuilder result = new StringBuilder();
            int emptySquares = 0;
            for (int i = 0; i < 64; i++)
            {
                Piece p = pieces[i];
                if(p == Piece.EMPTY)
                {
                    emptySquares++;
                } else
                {
                    if(emptySquares>0)
                    {
                        result.Append(emptySquares);
                        emptySquares = 0;
                    }
                    result.Append(fenPieceCodes[(int)p]);

                }
            }
            result.Append(this.turn);
            result.Append(this.whiteCanCastleQueenSide ? '1' : '0');
            result.Append(this.whiteCanCastleKingSide ? '1' : '0');
            result.Append(this.blackCanCastleQueenSide ? '1' : '0');
            result.Append(this.blackCanCastleKingSide ? '1' : '0');
            return result.ToString();
        }
    }
}